﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections;
using System.ComponentModel;

namespace System.Windows.Forms;

/// <summary>
///  A collection of listview groups.
/// </summary>
[ListBindable(false)]
public class ListViewGroupCollection : IList
{
    private readonly ListView _listView;

    private List<ListViewGroup>? _list;

    internal ListViewGroupCollection(ListView listView)
    {
        _listView = listView;
    }

    public int Count => List.Count;

    object ICollection.SyncRoot => this;

    bool ICollection.IsSynchronized => true;

    bool IList.IsFixedSize => false;

    bool IList.IsReadOnly => false;

    private List<ListViewGroup> List => _list ??= [];

    public ListViewGroup this[int index]
    {
        get => List[index];
        set
        {
            ArgumentNullException.ThrowIfNull(value);

            if (List.Contains(value))
            {
                return;
            }

            CheckListViewItems(value);
            value.ListView = _listView;
            List[index] = value;
        }
    }

    public ListViewGroup? this[string key]
    {
        get
        {
            if (_list is null)
            {
                return null;
            }

            for (int i = 0; i < _list.Count; i++)
            {
                if (string.Equals(key, this[i].Name, StringComparison.CurrentCulture))
                {
                    return this[i];
                }
            }

            return null;
        }
        set
        {
            ArgumentNullException.ThrowIfNull(value);

            if (_list is null)
            {
                // nothing to do
                return;
            }

            int index = -1;
            for (int i = 0; i < _list.Count; i++)
            {
                if (string.Equals(key, this[i].Name, StringComparison.CurrentCulture))
                {
                    index = i;
                    break;
                }
            }

            if (index != -1)
            {
                _list[index] = value;
            }
        }
    }

    object? IList.this[int index]
    {
        get => this[index];
        set
        {
            if (value is ListViewGroup group)
            {
                this[index] = group;
            }
        }
    }

    public int Add(ListViewGroup group)
    {
        ArgumentNullException.ThrowIfNull(group);
        ThrowInvalidOperationExceptionIfVirtualMode();

        if (Contains(group))
        {
            return -1;
        }

        CheckListViewItems(group);
        group.ListView = _listView;
        int index = ((IList)List).Add(group);
        if (_listView.IsHandleCreated)
        {
            _listView.InsertGroupInListView(List.Count, group);
            MoveGroupItems(group);
        }

        return index;
    }

    public ListViewGroup Add(string? key, string? headerText)
    {
        ListViewGroup group = new(key, headerText);
        Add(group);
        return group;
    }

    int IList.Add(object? value)
    {
        if (value is not ListViewGroup group)
        {
            throw new ArgumentException(SR.ListViewGroupCollectionBadListViewGroup, nameof(value));
        }

        return Add(group);
    }

    public void AddRange(params ListViewGroup[] groups)
    {
        ArgumentNullException.ThrowIfNull(groups);
        ThrowInvalidOperationExceptionIfVirtualMode();

        for (int i = 0; i < groups.Length; i++)
        {
            Add(groups[i]);
        }
    }

    public void AddRange(ListViewGroupCollection groups)
    {
        ArgumentNullException.ThrowIfNull(groups);
        ThrowInvalidOperationExceptionIfVirtualMode();

        for (int i = 0; i < groups.Count; i++)
        {
            Add(groups[i]);
        }
    }

    private void CheckListViewItems(ListViewGroup group)
    {
        for (int i = 0; i < group.Items.Count; i++)
        {
            ListViewItem item = group.Items[i];
            if (item.ListView is not null && item.ListView != _listView)
            {
                throw new ArgumentException(string.Format(SR.OnlyOneControl, item.Text));
            }
        }
    }

    public void Clear()
    {
        if (_listView.IsHandleCreated)
        {
            for (int i = 0; i < Count; i++)
            {
                _listView.RemoveGroupFromListView(this[i]);
            }
        }

        // Dissociate groups from the ListView
        for (int i = 0; i < Count; i++)
        {
            this[i].ListView = null;
            this[i].ReleaseUiaProvider();
        }

        List.Clear();

        // we have to tell the listView that there are no more groups
        // so the list view knows to remove items from the default group
        _listView.UpdateGroupView();
    }

    public bool Contains(ListViewGroup value) => List.Contains(value);

    bool IList.Contains(object? value)
    {
        if (value is not ListViewGroup group)
        {
            return false;
        }

        return Contains(group);
    }

    public void CopyTo(Array array, int index) => ((ICollection)List).CopyTo(array, index);

    public IEnumerator GetEnumerator() => List.GetEnumerator();

    public int IndexOf(ListViewGroup value) => List.IndexOf(value);

    int IList.IndexOf(object? value)
    {
        if (value is not ListViewGroup group)
        {
            return -1;
        }

        return IndexOf(group);
    }

    public void Insert(int index, ListViewGroup group)
    {
        ArgumentNullException.ThrowIfNull(group);
        ThrowInvalidOperationExceptionIfVirtualMode();

        if (Contains(group))
        {
            return;
        }

        CheckListViewItems(group);
        group.ListView = _listView;
        List.Insert(index, group);
        if (_listView.IsHandleCreated)
        {
            _listView.InsertGroupInListView(index, group);
            MoveGroupItems(group);
        }
    }

    void IList.Insert(int index, object? value)
    {
        if (value is ListViewGroup group)
        {
            Insert(index, group);
        }
    }

    private void MoveGroupItems(ListViewGroup group)
    {
        Debug.Assert(_listView.IsHandleCreated, "MoveGroupItems pre-condition: listView handle must be created");

        foreach (ListViewItem item in group.Items)
        {
            if (item.ListView == _listView)
            {
                item.UpdateStateToListView(item.Index);
            }
        }
    }

    public void Remove(ListViewGroup group)
    {
        if (!List.Remove(group))
        {
            return;
        }

        group.ListView = null;
        group.ReleaseUiaProvider();

        if (_listView.IsHandleCreated)
        {
            _listView.RemoveGroupFromListView(group);
        }
    }

    void IList.Remove(object? value)
    {
        if (value is ListViewGroup group)
        {
            Remove(group);
        }
    }

    public void RemoveAt(int index) => Remove(this[index]);

    private void ThrowInvalidOperationExceptionIfVirtualMode()
    {
        if (_listView.VirtualMode)
        {
            throw new InvalidOperationException(SR.ListViewCannotAddGroupsToVirtualListView);
        }
    }
}
